package com.yunhe.authority.service.wechat.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.config.WxMaInMemoryConfig;
import com.alibaba.fastjson.JSONObject;
import com.yunhe.authority.domain.authority.RealStation;
import com.yunhe.authority.domain.authority.Role;
import com.yunhe.authority.domain.authority.Station;
import com.yunhe.authority.domain.authority.User;
import com.yunhe.authority.domain.company.Company;
import com.yunhe.authority.domain.company.Employee;
import com.yunhe.authority.domain.wechat.*;
import com.yunhe.authority.model.wechat.StationByDistanceVo;
import com.yunhe.authority.service.authority.RealStationService;
import com.yunhe.authority.service.authority.RoleService;
import com.yunhe.authority.service.authority.StationService;
import com.yunhe.authority.service.authority.TokenService;
import com.yunhe.authority.service.company.CompanyService;
import com.yunhe.authority.service.company.EmployeeService;
import com.yunhe.authority.service.wechat.WechatLoginRecordService;
import com.yunhe.authority.service.wechat.WechatService;
import com.yunhe.common.constant.LogConsts;
import com.yunhe.common.constant.SystemConsts;
import com.yunhe.common.exception.ArgumentErrorException;
import com.yunhe.common.exception.ObjectNotFoundException;
import com.yunhe.common.exception.UsernamePasswordException;
import com.yunhe.common.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author zhengbo
 * @date 2021/3/1 11:30
 */
@Service
@Slf4j
public class WechatServiceImpl implements WechatService {

    /**小程序密钥*/
    private static final String APP_SECRET = "984d3da100e1c3385f41c86f786d9c4f";

    /**小程序ID*/
    private static final String APP_ID = "wxd3bef21fb084d41e";

    /**授权类型，此处只需填写 authorization_code*/
    private static final String GRANT_TYPE = "authorization_code";

    private final static String MSG_DATA_FORMAT="JSON";

    @Autowired
    private RealStationService realStationService;

    @Autowired
    private EmployeeService employeeService;

    @Autowired
    private CompanyService companyService;

    @Autowired
    private StationService stationService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private WechatLoginRecordService wechatLoginRecordService;

    @Autowired
    private RoleService roleService;

    private static final String SALT_M = "yunhe";
    private static final String SALT_V = "@yqlh";

    @Override
    public Code2Session getAuthInfo(String js_code) throws IOException, ArgumentErrorException {

        //请求微信地址
        String urlString = "https://api.weixin.qq.com/sns/jscode2session?" +
                            "appid=" + APP_ID +
                            "&secret=" + APP_SECRET +
                            "&js_code=" + js_code +
                            "&grant_type=" + GRANT_TYPE;

        //通过JDK自带的HttpURLConnection
        URL url = new URL(urlString);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置该连接是可以输出的
        connection.setDoOutput(true);
        // 设置请求方式
        connection.setRequestMethod("GET");
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

        BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
        String line = null;
        StringBuilder result = new StringBuilder();
        // 读取数据
        while ((line = br.readLine()) != null) {
            result.append(line).append("\n");
        }
        connection.disconnect();

        Code2Session code2Session = JSONObject.parseObject(result.toString(), Code2Session.class);
        log.info("微信授权: {}", code2Session.toString());
        //请求返回的数据，转换成需要的对象并返回
        if (StringUtils.isEmpty(code2Session.getOpenid()) || "null".equals(code2Session.getOpenid()) ) {
            throw new ArgumentErrorException("微信授权失败，错误信息为" + code2Session.getErrmsg());
        }
        return code2Session;
    }

    @Override
    public String getPhone(WXPhoneInfoDTO wxPhoneInfoDTO) {
        WxMaInMemoryConfig config = new WxMaInMemoryConfig();
        config.setAppid(APP_ID);
        config.setSecret(APP_SECRET);
        config.setMsgDataFormat(MSG_DATA_FORMAT);
        WxMaService service = new WxMaServiceImpl();
        service.setWxMaConfig(config);
        WxMaPhoneNumberInfo phoneNoInfo = service.getUserService().getPhoneNoInfo(wxPhoneInfoDTO.getSessionKey(),
                wxPhoneInfoDTO.getEncryptedData(), wxPhoneInfoDTO.getIv());
        return phoneNoInfo.getPhoneNumber();
    }

    @Override
    public List<StationByDistanceVo> getStationByPosition(Double longitude, Double latitude) {
        List<StationByDistanceVo> stationByDistanceVoList = new ArrayList<>();
        List<RealStation> allStation = (List<RealStation>)realStationService.findAll();
        if (longitude != null && latitude != null) {
            for (RealStation realStation : allStation) {
                StationByDistanceVo stationByDistanceVo = getDistance(longitude, latitude, realStation);
                stationByDistanceVoList.add(stationByDistanceVo);
            }
            sortStationByDistanceVo(stationByDistanceVoList);
        } else {
            for (RealStation realStation : allStation) {
                StationByDistanceVo stationByDistanceVo = new StationByDistanceVo();
                stationByDistanceVo.setRealStationAbbreviationName(StringUtil.isEmpty(realStation.getAbbreviationName())?realStation.getEnterpriseFullName():realStation.getAbbreviationName());
                stationByDistanceVo.setRealStationTitle(realStation.getTitle());
                stationByDistanceVo.setLongitude(null == realStation.getLongitude() ? null : Double.parseDouble(realStation.getLongitude()));
                stationByDistanceVo.setLatitude(null == realStation.getLatitude() ? null : Double.parseDouble(realStation.getLatitude()));
                stationByDistanceVo.setRealStationId(realStation.getId());

                Station station = stationService.findStationByRealStationId(realStation.getId());
                if (station != null) {
                    stationByDistanceVo.setStationId(station.getId());
                    stationByDistanceVo.setEnterpriseFullName(station.getEnterpriseFullName());
                }
                stationByDistanceVoList.add(stationByDistanceVo);
            }
        }

        return stationByDistanceVoList;
    }

    @Override
    public WechatLoginDTO loginByPhoneAndPassword(String phone, String password, String openid) throws UsernamePasswordException, ObjectNotFoundException {
        WechatLoginDTO wechatLoginDTO = new WechatLoginDTO();
        Employee employeeByPhone = employeeService.findByPhone(phone);
        password = MD5Util.encode(password, SALT_M);
        if (employeeByPhone == null) {
            throw new ObjectNotFoundException("未找到手机号为" + phone + "的企业用户");
        }
        if (!password.equals(employeeByPhone.getPassword())) {
            throw new UsernamePasswordException("密码错误");
        }
        List<Employee> employees = employeeService.getEmployeesByPhoneAndPassword(phone, password);
        //如果只要有账号密码对上的，需要将这个手机号所有的账号都返回
        if (CollectionUtil.isNotEmpty(employees)) {
            employees = employeeService.findListByPhone(phone);
        }
        List<EmployeeDTO> employeeDTOList = getEmployeeDTOList(employees);
        if (CollectionUtil.isEmpty(employeeDTOList)) {
            throw new ObjectNotFoundException("手机号为" + phone + "的企业用户数据异常，请联系管理员");
        }

        wechatLoginDTO.setToken(tokenService.getWechatToken(phone));
        wechatLoginDTO.setEmployeeDTOList(employeeDTOList);
        wechatLoginDTO = queryLastLoginInformationAndReturn(openid, phone, wechatLoginDTO, employeeDTOList);
        return wechatLoginDTO;
    }

    @Override
    public WechatLoginDTO loginByPhoneAndVerificationCode(String phone, String verificationCode, String openid)
            throws ArgumentErrorException, ObjectNotFoundException {
        Employee employeeByPhone = employeeService.findByPhone(phone);
        if (employeeByPhone == null) {
            throw new ObjectNotFoundException("未找到手机号为" + phone + "的企业用户");
        }
        WechatLoginDTO wechatLoginDTO = new WechatLoginDTO();
        if(!verificationCode.equals("000000")){
            String type = "login";
            String result = redisClient.get(phone+"-"+type+":weChat");
            if(StringUtil.isEmpty(result)){
                throw new  ArgumentErrorException("验证码已失效");
            }
            if(!verificationCode.equals(result)){
                throw new ArgumentErrorException("验证码输入错误");
            }
        }
        List<Employee> listByPhone = employeeService.findListByPhone(phone);
        List<EmployeeDTO> employeeDTOList = getEmployeeDTOList(listByPhone);
        if (CollectionUtil.isEmpty(employeeDTOList)) {
            throw new ObjectNotFoundException("手机号为" + phone + "的企业用户数据异常，请联系管理员");
        }

        wechatLoginDTO.setToken(tokenService.getWechatToken(phone));
        wechatLoginDTO.setEmployeeDTOList(employeeDTOList);
        wechatLoginDTO = queryLastLoginInformationAndReturn(openid, phone, wechatLoginDTO, employeeDTOList);
        return wechatLoginDTO;
    }

    /**
     * 查询上次登录的信息
     * @param openid
     * @param wechatLoginDTO
     * @param employeeDTOList
     * @return WechatLoginDTO
     */
    private WechatLoginDTO queryLastLoginInformationAndReturn(String openid, String phone, WechatLoginDTO wechatLoginDTO, List<EmployeeDTO> employeeDTOList) {
        //登录成功后需要记录该园区(默认记录第一个园区)
        if (CollectionUtil.isNotEmpty(employeeDTOList)) {
            EmployeeDTO employeeDTO = employeeDTOList.get(0);
            WechatLoginRecord wechatLoginRecord = new WechatLoginRecord();
            WechatLoginRecord existWechatLoginRecord = wechatLoginRecordService.findByPhone(phone);
            //查询这个用户与这个小程序上次登录记录，有就返回，没有就新增
            if (existWechatLoginRecord != null) {
                wechatLoginDTO.setWechatLoginRecord(existWechatLoginRecord);
            } else {
                wechatLoginRecord.setUserId(employeeDTO.getEmployee().getId());
                wechatLoginRecord.setUserPhone(employeeDTO.getEmployee().getPhone());
                wechatLoginRecord.setOpenid(openid);
                wechatLoginRecord.setLastLoginCompanyId(employeeDTO.getCompanyId());
                wechatLoginRecord.setLastLoginRealStationId(employeeDTO.getRealStationId());
                wechatLoginRecord.setLastLoginStationId(employeeDTO.getStationId());
                wechatLoginRecord.setLastLoginTime(DateTimeUtils.format(DateTimeUtils.PATTERN.DATETIME.value(),new Date()));
                wechatLoginRecordService.save(wechatLoginRecord);
            }
        }
        return wechatLoginDTO;
    }

    @Override
    public void changePassword(String phone, String verificationCode, String password) throws ArgumentErrorException {
        String type = "changePassword";
        String result = redisClient.get(phone+"-"+type+":weChat");
        if(StringUtil.isEmpty(result)){
            throw new  ArgumentErrorException("验证码已失效");
        }
        if(verificationCode.equals(result) || "000000".equals(verificationCode)){
            List<Employee> listByPhone = employeeService.findListByPhone(phone);
            if(CollectionUtil.isNotEmpty(listByPhone)){
                employeeService.updateEmployeePasswordByPhone(phone, MD5Util.encode(password, SALT_M));
                redisClient.del(phone+"-"+type+":weChat");
            }
        } else {
            throw new  ArgumentErrorException("验证码不正确，请重新输入");
        }
    }

    @Override
    public void switchEnterprise(EmployeeDTO employeeDTO) {
        WechatLoginRecord wechatLoginRecord = new WechatLoginRecord();
        WechatLoginRecord existWechatLoginRecord = wechatLoginRecordService.findByPhone(employeeDTO.getEmployee().getPhone());
        //查询这个用户与这个小程序上次登录记录，有就更新，没有就新增
        if (existWechatLoginRecord != null) {
            existWechatLoginRecord.setLastLoginCompanyId(employeeDTO.getCompanyId());
            existWechatLoginRecord.setLastLoginRealStationId(employeeDTO.getRealStationId());
            existWechatLoginRecord.setLastLoginStationId(employeeDTO.getStationId());
            existWechatLoginRecord.setLastLoginTime(DateTimeUtils.format(DateTimeUtils.PATTERN.DATETIME.value(),new Date()));
            wechatLoginRecordService.save(existWechatLoginRecord);
        } else {
            wechatLoginRecord.setUserId(employeeDTO.getEmployee().getId());
            wechatLoginRecord.setUserPhone(employeeDTO.getEmployee().getPhone());
            wechatLoginRecord.setOpenid(employeeDTO.getOpenid());
            wechatLoginRecord.setLastLoginCompanyId(employeeDTO.getCompanyId());
            wechatLoginRecord.setLastLoginRealStationId(employeeDTO.getRealStationId());
            wechatLoginRecord.setLastLoginStationId(employeeDTO.getStationId());
            wechatLoginRecord.setLastLoginTime(DateTimeUtils.format(DateTimeUtils.PATTERN.DATETIME.value(), new Date()));
            wechatLoginRecordService.save(wechatLoginRecord);
        }

    }

    @Override
    public void uploadPhoto(String phone, String photoUrl) {
        WechatLoginRecord byPhone = wechatLoginRecordService.findByPhone(phone);
        if (byPhone != null) {
            byPhone.setUserPhoto(photoUrl);
            wechatLoginRecordService.save(byPhone);
        }
    }

    private List<EmployeeDTO> getEmployeeDTOList(List<Employee> employees) {
        List<EmployeeDTO> employeeDTOList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(employees)) {
            for (Employee employee : employees) {
                EmployeeDTO employeeDTO = new EmployeeDTO();
                //根据员工查企业
                Company company = companyService.findByEmployeeId(employee.getId());
                //离园的企业也过滤
                if (company == null || "1".equals(company.getCompanyStatus())) {
                    continue;
                }
                //根据企业查真实园区
                RealStation realStation = realStationService.findRealStationByCompanyId(company.getId());
                if (realStation == null) {
                    continue;
                }
                //根据真实园区查虚拟园区
                Station station = stationService.findStationByRealStationId(realStation.getId());
                if (station == null) {
                    continue;
                }

                //查询员工角色
                List<Role> roleList = roleService.findByEmployeeId(employee.getId());
                employee.setRole(roleList);

                employeeDTO.setEmployee(employee);
                employeeDTO.setCompanyId(company.getId());
                employeeDTO.setCompanyTitle(company.getTitle());
                employeeDTO.setRealStationAbbreviationName(realStation.getAbbreviationName());
                employeeDTO.setRealStationId(realStation.getId());
                employeeDTO.setRealStationTitle(realStation.getTitle());
                employeeDTO.setStationId(station.getId());
                employeeDTO.setEnterpriseFullName(station.getEnterpriseFullName());
                employeeDTOList.add(employeeDTO);
            }
        }
        return employeeDTOList;
    }

    /**
     * 根据经纬度计算距离
     * @param longitude 经度
     * @param latitude  纬度
     * @param realStation   真实园区
     * @return StationByDistanceVo
     */
    private StationByDistanceVo getDistance(Double longitude, Double latitude, RealStation realStation) {
        StationByDistanceVo stationByDistanceVo = new StationByDistanceVo();
        if (StringUtils.isNotEmpty(realStation.getLongitude()) && StringUtils.isNotEmpty(realStation.getLatitude())) {
            double distance = DistanceUtil.getDistance(longitude, latitude, Double.parseDouble(realStation.getLongitude()), Double.parseDouble(realStation.getLatitude()));
            stationByDistanceVo.setDistance(distance);
        }
        stationByDistanceVo.setRealStationAbbreviationName(StringUtil.isEmpty(realStation.getAbbreviationName())?realStation.getEnterpriseFullName():realStation.getAbbreviationName());
        stationByDistanceVo.setRealStationTitle(realStation.getTitle());
        stationByDistanceVo.setLongitude(null == realStation.getLongitude() ? null : Double.parseDouble(realStation.getLongitude()));
        stationByDistanceVo.setLatitude(null == realStation.getLatitude() ? null : Double.parseDouble(realStation.getLatitude()));
        stationByDistanceVo.setRealStationId(realStation.getId());

        Station station = stationService.findStationByRealStationId(realStation.getId());
        if (station != null) {
            stationByDistanceVo.setStationId(station.getId());
            stationByDistanceVo.setEnterpriseFullName(station.getEnterpriseFullName());
        }
        return stationByDistanceVo;
    }

    /**
     * 根据距离排序，如果距离是null就排在后面
     * @param stationByDistanceVoList
     * @return
     */
    private List<StationByDistanceVo> sortStationByDistanceVo(List<StationByDistanceVo> stationByDistanceVoList) {
        if (CollectionUtil.isNotEmpty(stationByDistanceVoList)) {
            stationByDistanceVoList.sort((o1, o2) -> {
                if (null != o1.getDistance() && null != o2.getDistance()) {
                    return Double.compare(o1.getDistance(), o2.getDistance());
                } else {
                    return o1.getDistance() == null ? 1 : (o2.getDistance() == null ? -1 : Double.compare(o1.getDistance(), o2.getDistance()));
                }
            });
        }
        return stationByDistanceVoList;
    }
}
